using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Godot;

using MatchThree.Enums;

public partial class MainGame : Control
{
    // 预制场景
    [Export] public PackedScene GridScene;

    [Export] public PackedScene SweetScene;

    [Export] public int GridSize = 40;

    [Export] public int GridNumber = 10;

    private Sweet[,] Sweets { get; set; }
    private readonly List<CookieType> _cookieTypes = Enum.GetValues<CookieType>().ToList();
    private readonly Random _random = new Random();
    private bool MouseButtonLeftPressed { get; set; } = false;

    private Sweet SelectSweet { get; set; }

    public override void _Ready()
    {
        Sweets = new Sweet[GridNumber, GridNumber];
        InitGrid();
        InitCookie();
    }

    public void SweetClearAction()
    {
        GD.Print("aaaaaaaa");
    }

    public override void _Process(double delta)
    {
    }

    public override void _PhysicsProcess(double delta)
    {
        if (Input.IsMouseButtonPressed(MouseButton.Left))
        {
            if (MouseButtonLeftPressed)
            {
                return;
            }
            var sweet = FindSweet();
            ChangeSelectSweet(sweet);

            GD.Print($"选择：{SelectSweet.X},{SelectSweet.Y}");
            MouseButtonLeftPressed = true;
        }
        else
        {
            MouseButtonLeftPressed = false;
        }
    }

    private void CheckError()
    {
        var hasError = false;
        var errors = new List<string>();
        for (var j = 0; j < GridNumber; j++)
        {
            var temp = "";
            for (var i = 0; i < GridNumber; i++)
            {
                temp += $"X:{Sweets[i, j].X},Y:{Sweets[i, j].Y}";
                if (i != Sweets[i, j].X || j != Sweets[i, j].Y)
                {
                    GD.Print($"这里错了，I:{i},X：{Sweets[i, j].X},J:{j},Y:{Sweets[i, j].Y}");
                    hasError = true;
                }
            }
            errors.Add(temp);
        }
        if (hasError)
        {
            foreach (var item in errors)
            {
                GD.Print(item);
            }
        }
    }

    private async Task ChangeSelectSweet(Sweet sweet)
    {
        if (sweet is null)
        {
            return;
        }

        if (SelectSweet == sweet)
        {
            await ScaleSweet(sweet, false);
            SelectSweet = null;
        }
        else
        {
            if (SelectSweet is null)
            {
                SelectSweet = sweet;
                await ScaleSweet(sweet, true);
            }
            else
            {
                if (SelectSweet.IsAdjoin(sweet))
                {
                    GD.Print("交换前");
                    CheckError();
                    await SwapSweets(SelectSweet, sweet);
                    GD.Print("交换后");

                    CheckError();
                    var hasMatch = await ExecMatch();
                    if (!hasMatch)
                    {
                        await SwapSweets(SelectSweet, sweet);
                    }
                }
                else
                {
                    await ScaleSweet(SelectSweet, false);
                    SelectSweet = sweet;
                    await ScaleSweet(sweet, true);
                }
            }
        }
    }

    private async Task ScaleSweet(Sweet sweet, bool isMagnify)
    {
        var vector = new Vector2(1, 1);
        if (isMagnify)
        {
            vector = new Vector2(1.1f, 1.1f);
        }
        using var tween = CreateTween();
        tween.Parallel().TweenProperty(SelectSweet, "scale", vector, 0.2f);
        await ToSignal(tween, Tweener.SignalName.Finished);
    }

    private async Task SwapSweets(Sweet sweetA, Sweet sweetB)
    {
        var (x, y) = (sweetA.X, sweetA.Y);

        sweetA.SetGrid(sweetB.X, sweetB.Y);
        sweetB.SetGrid(x, y);

        Sweets[sweetB.X, sweetB.Y] = sweetB;
        Sweets[sweetA.X, sweetA.Y] = sweetA;

        var positionA = sweetA.Position;
        using var tween = CreateTween();
        tween.Parallel().TweenProperty(sweetA, "position", sweetB.Position, 0.2f);
        tween.Parallel().TweenProperty(sweetB, "position", positionA, 0.2f);
        await ToSignal(tween, Tweener.SignalName.Finished);
    }

    private async Task<bool> ExecMatch()
    {
        var result = false;
        GD.Print("匹配前");
        CheckError();

        var sweets = MatchSweets();
        while (sweets.Count != 0)
        {
            result = true;
            GD.Print("清理前");
            await ClearSweets(sweets);
            GD.Print("清理后");
            await FillSweets();
            sweets = MatchSweets();
        }

        if (result)
        {
            SelectSweet = null;
        }

        GD.Print("匹配后");
        CheckError();
        return result;
    }

    private async Task FillSweets()
    {
        var tasks = new List<Task>();

        for (var x = 0; x < GridNumber; x++)
        {
            tasks.Add(FillColumnsSweet(x));
        }
        await Task.WhenAll(tasks);
    }

    private async Task FillColumnsSweet(int x)
    {
        for (var y = GridNumber - 1; y >= 0; y--)
        {
            if (Sweets[x, y] is null)
            {
                await MoveDownSweets(x, y);
            }
        }
    }

    private async Task MoveDownSweets(int x, int emptyIndexY)
    {
        using var tween = CreateTween();

        Sweet matchSweet = null;

        for (var y = emptyIndexY - 1; y >= 0; y--)
        {
            if (Sweets[x, y] is not null)
            {
                matchSweet = Sweets[x, y];
                break;
            }
        }

        if (matchSweet is null)
        {
            matchSweet = CreateSweet(x, -1);
            matchSweet.SetGrid(x, -1);
            AddChild(matchSweet);
        }

        MoveDownSweet(tween, matchSweet, emptyIndexY);
    }

    private void MoveDownSweet(Tween tween, Sweet sweet, int y)
    {
        for (var i = sweet.Y; i <= y; i++)
        {
            tween.SetParallel().TweenProperty(sweet, "position", new Vector2(sweet.X * GridSize, i * GridSize), 0.2f);
        }
        if (sweet.Y >= 0)
        {
            Sweets[sweet.X, sweet.Y] = null;
        }
        Sweets[sweet.X, y] = sweet;
        sweet.SetGrid(sweet.X, y);
    }

    private async Task AddSweet(int x, int y, float duration)
    {
        var tween = CreateTween();
        var sweet = CreateSweet(x, y - GridNumber);
        sweet.SetGrid(x, y);
        AddChild(sweet);
        Sweets[x, y] = sweet;
        tween.TweenProperty(sweet, "position", new Vector2(x * GridSize, y * GridSize), duration);
        await ToSignal(tween, Tweener.SignalName.Finished);
    }

    private Sweet CreateSweet(int x, int y)
    {
        var sweet = (Sweet)ResourceLoader.Load<PackedScene>(SweetScene.ResourcePath).Instantiate();
        var nextCookieType = _cookieTypes[_random.Next(_cookieTypes.Count)];
        sweet.SetCookieTexture(nextCookieType);
        //cookie.Size = new Vector2(GridSize, GridSize);
        sweet.Position = new Vector2(x * GridSize, y * GridSize);
        sweet.SweetClear += SweetClearAction;
        return sweet;
    }

    private async Task ClearSweets(List<Sweet> sweets)
    {
        var tasks = new List<Task>();
        foreach (var item in sweets)
        {
            Sweets[item.X, item.Y] = null;
            tasks.Add(item.Die());
        }
        await Task.WhenAll(tasks);

        SelectSweet = null;
    }

    private List<Sweet> MatchSweets()
    {
        var result = new List<Sweet>();
        for (var i = 0; i < GridNumber; i++)
        {
            for (var j = 0; j < GridNumber - 2; j++)
            {
                if (Sweets[i, j].EqualsType(Sweets[i, j + 1]) && Sweets[i, j].EqualsType(Sweets[i, j + 2]))
                {
                    result.Add(Sweets[i, j]);
                    result.Add(Sweets[i, j + 1]);
                    result.Add(Sweets[i, j + 2]);
                }
            }
        }

        for (var j = 0; j < GridNumber; j++)
        {
            for (var i = 0; i < GridNumber - 2; i++)
            {
                if (Sweets[i, j].EqualsType(Sweets[i + 1, j]) && Sweets[i, j].EqualsType(Sweets[i + 2, j]))
                {
                    result.Add(Sweets[i, j]);
                    result.Add(Sweets[i + 1, j]);
                    result.Add(Sweets[i + 2, j]);
                }
            }
        }

        return [.. result.Distinct()];
    }

    private async Task InitCookie()
    {
        var tasks = new List<Task>();

        for (var y = GridNumber - 1; y >= 0; y--)
        {
            for (var x = 0; x < GridNumber; x++)
            {
                tasks.Add(AddSweet(x, y, 2f));
            }
        }
        await Task.WhenAll(tasks);
        await ExecMatch();
    }

    private void InitGrid()
    {
        for (var i = 0; i < GridNumber; i++)
        {
            for (var j = 0; j < GridNumber; j++)
            {
                var grid = (Grid)ResourceLoader.Load<PackedScene>(GridScene.ResourcePath).Instantiate();
                grid.Size = new Vector2(GridSize, GridSize);
                grid.Position = new Vector2(i * GridSize, j * GridSize);
                AddChild(grid);
            }
        }
    }

    private Sweet FindSweet()
    {
        var mousePoint = GetViewport().GetMousePosition();
        foreach (var item in GetChildren())
        {
            if (item is Sweet sweet)
            {
                if (sweet.GetGlobalRect().HasPoint(mousePoint))
                {
                    return sweet;
                }
            }
        }
        return null;
    }
}